package com.stxx.stiot2.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.stxx.framwork.constants.FluxDataType;
import com.stxx.framwork.domain.model.Option;
import com.stxx.framwork.domain.model.PageQuery;
import com.stxx.framwork.domain.model.PageResult;
import com.stxx.framwork.exception.ServiceException;
import com.stxx.framwork.tran.TranUtils;
import com.stxx.framwork.utils.FluxUtils;
import com.stxx.framwork.utils.ObjectUtils;
import com.stxx.framwork.utils.StoreUtils;
import com.stxx.stiot2.constants.RedisKeys;
import com.stxx.stiot2.constants.SystemConstants;
import com.stxx.stiot2.domain.Device;
import com.stxx.stiot2.domain.Terminal;
import com.stxx.stiot2.domain.cache.TerminalCache;
import com.stxx.stiot2.domain.ext.SignalEx;
import com.stxx.stiot2.external.RedisServer;
import com.stxx.stiot2.service.ProfileService;
import com.stxx.stiot2.service.TerminalService;
import com.stxx.stiot2.service.tran.DeviceTranService;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.sql.SqlBuilder;

import java.sql.SQLException;
import java.util.*;

/**
 * @author Fuzhenlei
 * @date 2025/01/15 16:41
 */
@Component
public class TerminalServiceImpl implements TerminalService {
    @Inject
    StoreUtils storeUtils;
    @Inject
    RedisServer redisServer;
    @Inject
    ProfileService profileService;
    @Inject
    DeviceTranService deviceTranService;


    public String getKey(String id){
        return RedisKeys.terminal_info+id;
    }

    @Override
    public TerminalCache getCache(String id){
        String key = getKey(id);
        TerminalCache cache = redisServer.getObj(key,TerminalCache.class);
        if(cache!=null){
            return cache;
        }
        Terminal bean = this.queryById(id);
        if(bean==null){
            return null;
        }
        cache = BeanUtil.toBean(bean,TerminalCache.class);
        redisServer.setObj(key,cache);
        return cache;
    }

    private TranUtils getTranService(){

            return TranUtils.build().add("deviceId",deviceTranService);

    }

    @Override
    public Terminal queryById(String id) {
        try {
            Terminal terminal = storeUtils.selectById(id, Terminal.class);
            getTranService().tran(terminal);
            return terminal;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<Terminal> queryByIds(Collection<String> ids) {
        try {
            List<Terminal> list = storeUtils.selectByIds(ids, Terminal.class);
            getTranService().tran(list);
            return list;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int insert(Terminal bean) throws SQLException {
        validEntityBeforeSave(bean);
        return storeUtils.insert(bean);
    }

    @Override
    public int update(Terminal bean) throws SQLException {
        validEntityBeforeSave(bean);
        String key = getKey(bean.getCode());
        redisServer.delete(key);
        int i = storeUtils.update(bean);
        if(i>0){
            redisServer.setObj(getKey(bean.getCode()),BeanUtil.toBean(bean,TerminalCache.class));
        }
        return i;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(Terminal bean){

    }




    @Override
    public int delete(String name) throws SQLException {
        redisServer.delete(getKey(name));
        return storeUtils.delete(Terminal.class, name);
    }

    @Override
    public int deleteByIds(String[] ids) throws SQLException {
        String[] keys = new String[ids.length];
        for(int i =0;i<ids.length;i++){
            keys[i] = getKey(ids[i]);
        }
        redisServer.delete(keys);
        return storeUtils.deleteByIds(Terminal.class,ids);
    }

    @Override
    public List<Terminal> list(Terminal obj) throws SQLException {
        List<Terminal> list = storeUtils.selectList(Terminal.class,obj);
        getTranService().tran(list);
        return list;
    }

    @Override
    public PageResult<Terminal> pageList(Terminal obj, PageQuery pageQuery) throws SQLException {
        PageResult<Terminal> result = storeUtils.selectPageList(Terminal.class,obj,pageQuery);
        getTranService().tran(result.getData());
        return result;
    }

    @Override
    public List<TerminalCache> queryGwAndCodeByProfile(String profileCode) {
        SqlBuilder sb = new SqlBuilder();
        sb.append("select gateway_code,code from "+storeUtils.getTableName(Terminal.class)+" where profile_code = ?", profileCode);
        try {
            return storeUtils.sql(sb).queryRowList(TerminalCache.class);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }




    @Override
    public List<Option> queryOptions(String id, String w, String pid) throws SQLException {
        SqlBuilder sb = new SqlBuilder();
        sb.append("select `code` as value,`name` as label from "+storeUtils.getTableName(Terminal.class));
        if(id != null){
            sb.append(" where code=?",id);
        }else{
            sb.append(" where (code like concat('%',?,'%') or name like concat('%',?,'%'))",w,w);
            if(StrUtil.isNotEmpty(pid)){
                sb.append(" and profile_code = ?",pid);
            }
        }
        sb.append(" limit "+ SystemConstants.options_query_max_size);
        return storeUtils.sql(sb).queryRowList(Option.class);
    }

    @Override
    public List<Option> queryOptions(String[] ids) throws SQLException {
        SqlBuilder sb = new SqlBuilder();
        sb.append("select code as value,name as label from "+storeUtils.getTableName(Terminal.class)+" where code in (");
        sb.append(StrUtil.repeat("?,",ids.length));
        sb.removeLast();
        sb.append(")",ids);
        return storeUtils.sql(sb).queryRowList(Option.class);
    }

    @Override
    public String getProfile(String code){
       TerminalCache terminal = this.getCache(code);
       if(terminal==null){
           return null;
       }
       return terminal.getProfileCode();
    }

    @Override
    public FluxDataType getFieldType(String terminalCode, String signalCode){
        String profile = this.getProfile(terminalCode);
        Map<String, SignalEx> signals = profileService.signals(profile);
        if(signals.containsKey(signalCode)){
            return signals.get(signalCode).getFieldType();
        }
        return FluxDataType.STRING;
    }

    @Override
    public Object getLastValue(String terminalCode, String signalCode){
        Class<?> clazz = FluxUtils.getType(this.getFieldType(terminalCode,signalCode));
        return redisServer.getValue(RedisKeys.terminal_data+terminalCode,signalCode,clazz);
    }

    @Override
    public void setLastValue(String terminalCode, String signalCode, Object value){
        redisServer.setValue(RedisKeys.terminal_data+terminalCode,signalCode,value);
    }

    @Override
    public void setLastValues(String terminalCode, Map<String, Object> data) {
        Map<String,String> data2 = new HashMap<>();
        data.forEach((key,value)->{
            if(value==null||value.toString().isEmpty()){
                return;
            }
            data2.put(key,value.toString());
        });
        redisServer.setValues(RedisKeys.terminal_data+terminalCode,data2);
    }

    @Override
    public Map<String,Object> getLastValues(String terminalCode){
        Map<String,String> values = redisServer.getValues(RedisKeys.terminal_data+terminalCode);
        Map<String,Object> map;
        if(values!=null){
            map = new HashMap<>(values.size());
            values.forEach((k,v)->{
                Class<?> fieldType = FluxUtils.getType(getFieldType(terminalCode,k));
                map.put(k, ObjectUtils.get(v,fieldType));
            });
        } else {
            map = null;
        }


        return map;
    }


    @Override
    public int updateLastReportTime(TerminalCache terminalCache) throws SQLException {
        redisServer.setObj(getKey(terminalCache.getCode()),terminalCache);
        SqlBuilder sb = new SqlBuilder();
        sb.append("update ")
                .append(StoreUtils.getTableName("terminal")).append(" set last_report_time=? where code = ?",terminalCache.getLastReportTime(),terminalCache.getCode());
        return storeUtils.sql(sb).update();
    }

    @Override
    public PageResult<Terminal> listBySw(String sw, PageQuery pageQuery) throws SQLException {
        SqlBuilder sb = new SqlBuilder();
        sb.append("from ").append(StoreUtils.getTableName("terminal"))
                .append(" where device_id is null  and (code like concat('%',?,'%')",sw)
                .append(" or name like concat('%',?,'%'))",sw);
        sb.backup();
        sb.insert("select * ");
        if(StrUtil.isNotEmpty(pageQuery.getOrderByColumn())){
            if(StrUtil.isBlank(pageQuery.getIsAsc())){
                pageQuery.setIsAsc("asc");
            }
            sb.append(" order by ")
                    .append(pageQuery.getOrderByColumn())
                    .append(" ")
                    .append(pageQuery.getIsAsc());
        }
        sb.append(" limit ?,? ", (pageQuery.getPageNum()-1)*pageQuery.getPageSize(),pageQuery.getPageSize()); //分页获取列表

        List<Terminal> list = storeUtils.sql(sb).queryRowList(Terminal.class);
        //回滚（可以复用备份前的代码构建）
        sb.restore();
        sb.insert("select count(*)  ");
        //查询总数
        Long total = Long.parseLong(storeUtils.sql(sb).queryValue().toString());
        PageResult<Terminal> pageResult = new PageResult(list,total);
        return pageResult;
    }

    @Override
    public void association(Long deviceId, Collection<String> terminals) throws SQLException {

        List<String> codeList = storeUtils.sql("select code from "+StoreUtils.getTableName("terminal")+" where device_id=?",deviceId).queryValueList();
        Set<String> codeSet = new HashSet<>();
        if(codeList!=null){
            codeSet.addAll(codeList);
        }
        codeSet.addAll(terminals);
        codeSet.forEach(item->{
            redisServer.delete(getKey(item));
        });
        SqlBuilder sb = new SqlBuilder();
        sb.append("update ")
                .append(StoreUtils.getTableName("terminal"))
                .append(" set device_id=null where device_id=?",deviceId);
        storeUtils.sql(sb).update();
        sb.clear();
        sb.append("update ")
                .append(StoreUtils.getTableName("terminal"))
                .append(" set device_id=? where code in (?...)",deviceId,terminals);
        storeUtils.sql(sb).update();
    }

    @Override
    public List<Terminal> queryByDeviceId(Long deviceId) throws SQLException {
        SqlBuilder sb = new SqlBuilder();
        sb.append("select * from "+storeUtils.getTableName(Terminal.class)+" where device_id =?",deviceId);
        return storeUtils.sql(sb).queryRowList(Terminal.class);
    }

    @Override
    public void createDevice(Terminal terminal) throws SQLException {
        if(terminal.getDeviceId()!=null){
            throw new ServiceException("终端已绑定设备"+terminal.getCode());
        }
        Device device = new Device();
        //device.setDeviceId();
        device.setName(terminal.getName());
        device.setCategory(terminal.getCategory());
        device.setRemark(terminal.getRemark());
        storeUtils.insert(device);
        terminal.setDeviceId(device.getDeviceId());
        this.update(terminal);
    }
}
